/*
 * ==================================================================== The Apache Software License, Version 1.1
 * Copyright (c) 2001-2005 The Apache Software Foundation. All rights reserved. Redistribution and use in source and
 * binary forms, with or without modification, are permitted provided that the following conditions are met: 1.
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 * disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user
 * documentation included with the redistribution, if any, must include the following acknowledgment: "This product
 * includes software developed by the Apache Software Foundation (http://www.apache.org/)." Alternately, this
 * acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear.
 * 4. The names "Apache" and "Apache Software Foundation" and "Apache Commons" must not be used to endorse or promote
 * products derived from this software without prior written permission. For written permission, please contact
 * apache@apache.org. 5. Products derived from this software may not be called "Apache", nor may "Apache" appear in
 * their name, without prior written permission of the Apache Software Foundation. THIS SOFTWARE IS PROVIDED ``AS IS''
 * AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ==================================================================== This software consists of voluntary
 * contributions made by many individuals on behalf of the Apache Software Foundation. For more information on the
 * Apache Software Foundation, please see <http://www.apache.org/>.
 */

package org.apache.commons.net.nntp;

import java.util.ArrayList;
import java.util.StringTokenizer;

/**
 * This is a class that contains the basic state needed for message retrieval and threading.
 * With thanks to Jamie  Zawinski <jwz@jwz.org>
 * @author rwinston <rwinston@checkfree.com>
 *
 */
public class Article implements Threadable {
    /**
     * Recursive method that traverses a pre-threaded graph (or tree) 
     * of connected Article objects and prints them out.  
     * @param article the root of the article 'tree'
     * @param depth the current tree depth
     */
    public static void printThread(Article article, int depth) {
        for(int i = 0; i < depth; ++i)
            System.out.print("==>");
        System.out.println(article.getSubject() + "\t" + article.getFrom());
        if(article.kid != null)
            printThread(article.kid, depth + 1);
        if(article.next != null)
            printThread(article.next, depth);
    }
    public Article kid, next;
    private int articleNumber;
    private String subject;
    private String date;
    private String articleId;
    private String simplifiedSubject;
    private String from;
    private StringBuffer header;

    private StringBuffer references;

    private boolean isReply = false;

    public Article() {
        header = new StringBuffer();
    }

    /**
     * Adds an arbitrary header key and value to this message's header.
     * @param name the header name
     * @param val the header value
     */
    public void addHeaderField(String name, String val) {
        header.append(name);
        header.append(": ");
        header.append(val);
        header.append('\n');
    }

    /**
     * Adds a message-id to the list of messages that this message references (i.e. replies to)
     * @param msgId
     */
    public void addReference(String msgId) {
        if(references == null){
            references = new StringBuffer();
            references.append("References: ");
        }
        references.append(msgId);
        references.append("\t");
    }

    public String getArticleId() {
        return articleId;
    }

    public int getArticleNumber() {
        return articleNumber;
    }

    public String getDate() {
        return date;
    }

    public String getFrom() {
        return from;
    }

    /**
     * Returns the MessageId references as an array of Strings
     * @return an array of message-ids
     */
    public String[] getReferences() {
        if(references == null)
            return new String[0];
        ArrayList list = new ArrayList();
        int terminator = references.toString().indexOf(':');
        StringTokenizer st = new StringTokenizer(references.substring(terminator), "\t");
        while(st.hasMoreTokens()){
            list.add(st.nextToken());
        }
        return (String[])list.toArray();
    }

    public String getSubject() {
        return subject;
    }

    public boolean isDummy() {
        return (getSubject() == null);
    }

    public Threadable makeDummy() {
        return (Threadable)new Article();
    }

    public String messageThreadId() {
        return articleId;
    }

    public String[] messageThreadReferences() {
        return getReferences();
    }

    public void setArticleId(String string) {
        articleId = string;
    }

    public void setArticleNumber(int i) {
        articleNumber = i;
    }

    public void setChild(Threadable child) {
        this.kid = (Article)child;
        flushSubjectCache();
    }

    public void setDate(String string) {
        date = string;
    }

    public void setFrom(String string) {
        from = string;
    }

    public void setNext(Threadable next) {
        this.next = (Article)next;
        flushSubjectCache();
    }

    public void setSubject(String string) {
        subject = string;
    }

    public String simplifiedSubject() {
        if(simplifiedSubject == null)
            simplifySubject();
        return simplifiedSubject;
    }

    public boolean subjectIsReply() {
        if(simplifiedSubject == null)
            simplifySubject();
        return isReply;
    }

    private void flushSubjectCache() {
        simplifiedSubject = null;
    }

    /**
     * Attempts to parse the subject line for some typical reply signatures, and strip them out
     *
     */
    private void simplifySubject() {
        int start = 0;
        String subject = getSubject();
        int len = subject.length();

        boolean done = false;

        while( !done){
            done = true;

            // skip whitespace
            // "Re: " breaks this
            while(start < len && subject.charAt(start) == ' '){
                start++;
            }

            if(start < (len - 2) && (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
               && (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')){

                if(subject.charAt(start + 2) == ':'){
                    start += 3; // Skip "Re:"
                    isReply = true;
                    done = false;
                }
                else if(start < (len - 2) && (subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')){

                    int i = start + 3;

                    while(i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9')
                        i++;

                    if(i < (len - 1) && (subject.charAt(i) == ']' || subject.charAt(i) == ')')
                       && subject.charAt(i + 1) == ':'){
                        start = i + 2;
                        isReply = true;
                        done = false;
                    }
                }
            }

            if(simplifiedSubject == "(no subject)")
                simplifiedSubject = "";

            int end = len;

            while(end > start && subject.charAt(end - 1) < ' ')
                end--;

            if(start == 0 && end == len)
                simplifiedSubject = subject;
            else
                simplifiedSubject = subject.substring(start, end);
        }
    }
}